﻿namespace MapleRock.P2PEmulator.RGR
{
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Linq;
  using System.Text;
  using System.Windows.Forms;

  internal sealed class ExperimentLog
  {
    private delegate void AddTextCallback(string text);

    private delegate void SetMaxValueCallback(int value);

    private delegate void IncrementProgressCallback();

    static ExperimentLog()
    {
      ExperimentLog.Instance = new ExperimentLog();
    }

    private TextBox logTb = null;

    private Label timeLabel = null;

    private ProgressBar pb = null;

    private UserControl cntl = null;

    private bool wasProgressBarConfigured = false;

    private DateTime previousRunCompletedTimestamp = DateTime.Now;

    public static ExperimentLog Instance { get; private set; }

    public void ConfigureProgressBar(int maxValue)
    {
      Debug.Assert(maxValue > 1, "maxValue > 1");

      if (this.cntl.InvokeRequired)
      {
        var callback = new SetMaxValueCallback(this.ProgressBar_SetMaxValue);
        this.cntl.Invoke(callback, new object[] { maxValue });
      }
      else
      {
        this.ProgressBar_SetMaxValue(maxValue);
      }

      this.wasProgressBarConfigured = true;
    }

    public void IncrementRunProgress()
    {
      Debug.Assert(this.wasProgressBarConfigured, "this.wasProgressBarConfigured");

      if (this.cntl.InvokeRequired)
      {
        var callback = new IncrementProgressCallback(this.ProgressBar_Increment);
        this.cntl.Invoke(callback, new object[] { });
      }
      else
      {
        this.ProgressBar_Increment();
      }
    }

    public void LogEvent(string fmt, params object[] args)
    {
      Debug.Assert(this.wasProgressBarConfigured, "this.wasProgressBarConfigured");

      var eventText = string.Format(fmt, args);

      if (this.cntl.InvokeRequired)
      {
        var callback = new AddTextCallback(this.TextBox_AddText);
        this.cntl.Invoke(callback, new object[] { eventText });
      }
      else
      {
        this.TextBox_AddText(eventText);
      }
    }

    public void SetLogControls(UserControl cntl, TextBox logTb, Label timeLabel, ProgressBar pb)
    {
      Debug.Assert(timeLabel != null, "timeLabel != null");
      this.timeLabel = timeLabel;

      Debug.Assert(logTb != null, "logTb != null");
      this.logTb = logTb;

      Debug.Assert(pb != null, "pb != null");
      this.pb = pb;

      Debug.Assert(cntl != null, "cntl != null");
      this.cntl = cntl;
    }

    private void TextBox_AddText(string eventText)
    {
      var timeText = string.Format("{0}: {1}\r\n", DateTime.Now.ToShortTimeString(), eventText);

      this.logTb.Text += timeText;
      this.logTb.Select(this.logTb.Text.Length, 0);
      this.logTb.ScrollToCaret();
    }

    private void ProgressBar_Increment()
    {
      Debug.Assert(!this.pb.InvokeRequired, "!this.pb.InvokeRequired");
      ++this.pb.Value;

      var lastRunDuration = (DateTime.Now - this.previousRunCompletedTimestamp);
      var estimatedTotalDuration = TimeSpan.Zero;
      int runsCompleted = this.pb.Maximum - this.pb.Value;
      for (var j = 0; j < runsCompleted; ++j)
      {
        estimatedTotalDuration += lastRunDuration;
      }

      Debug.Assert(!this.timeLabel.InvokeRequired, "!this.timeLabel.InvokeRequired");
      this.timeLabel.Text = string.Format(
        "{0} мин {1} сек",
        estimatedTotalDuration.Minutes,
        estimatedTotalDuration.Seconds); 

      previousRunCompletedTimestamp = DateTime.Now;
    }

    private void ProgressBar_SetMaxValue(int value)
    { 
      Debug.Assert(!this.pb.InvokeRequired, "!this.pb.InvokeRequired");
      Debug.Assert(value > 1, "value > 1");

      this.pb.Maximum = value;
    }
  }
}
